package com.cashmanager.client.loader;

import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import javax.imageio.ImageIO;

class Uint32{
	byte[] bts;
	public Uint32(byte fi, byte s, byte t, byte fo){
		bts = new byte[4];
		bts[0] = fi;
		bts[1] = s;
		bts[2] = t;
		bts[3] = fo;
	}
	public int toNum(){
		ByteBuffer bb = ByteBuffer.wrap(bts);
		bb.order(ByteOrder.LITTLE_ENDIAN);
		return bb.getInt();
	}
}

class Int16 {
	byte[] bts;
	public Int16(byte first, byte second){
		bts = new byte[2];
		bts[0] = second;
		bts[1] = first;
	}
	public int toNum(){
		return byteArrayToInt(bts);
	}
	
	public static int byteArrayToInt(byte[] b){
		int value = 0;
		for (int i = 0; i < 2; i ++){
			value = (value << 8) | b[i];
		}
		return value;
	}
}
class Uint16 {
	byte[] bts;
	
	public Uint16 (byte first, byte second){
		bts = new byte[2];
		bts[0] = first;
		bts[1] = second;
	}
	
	public int toNum(){
		int i = 0;
		i |= bts[1] & 0xFF;
		i <<= 8;
		i |= bts[0] & 0xFF;
		return i;
	}
	
	public static void copyToIntArray(Uint16[] from, int[] to){
		if (from.length != to.length){
			P.ptl("length not match");
		}
		for (int i = 0, len = to.length; i < len; i ++){
			to[i] = from[i].toNum();
		}
	}
	
	private static Uint16[] getn(byte[]b, int n, int spos){
		Uint16 unit[] = new Uint16[n];
		for (int i = 0; i < n; i ++){
			unit[i] = new Uint16(b[spos+i*2],b[spos+i*2 +1]);
		}
		return unit;
	}
	
	public static Uint16 get2(byte[] b, int spos){
		Uint16 unit = new Uint16(b[spos], b[spos+1]);
		return unit;
	}
	
	public static Uint16[] get4(byte[] b, int spos){
		return getn(b, 4, spos);
	}
	public static Uint16[] get6(byte[] b, int spos){
		return getn(b, 6, spos);
	}
	public static Uint16[] get12(byte[] b, int spos){
		return getn(b, 12, spos);
	}
	public static Uint16[] get24(byte[] b, int spos){
		return getn(b, 24, spos);
	}
	public static String arrayTostring(Uint16[] units){
		StringBuffer sb = new StringBuffer();
		for (int i = 0, len = units.length; i < len; i ++){
			if (units[i].toNum() != 0){
				sb.append(units[i].toChar());
			}
		}
		return sb.toString();
	}
	
	public char toChar(){
		return (char) bts[0];
	}
	
	public int toDate(){
		int num = toNum();
		int y = num/512;
		int m = (num - y * 512)/32;
		int d = num - y * 512 - m *32;
		return (y + 1980) * 10000 + m * 100 + d;
	}
	
	public String toTime(){
		int num = toNum();
		int h = num/2048;
		int m = (num - h * 2048)/32;
		int d = (num - h * 2048 - m * 32);
		
		return h + ":" + m + ":" +d;
	}
}

class P {
	public static void pt(Object o){
		System.out.print(o);
	}
	public static void ptl(Object o){
		System.out.println(o);
	}
	
}
class Header{
	int headStart[];
	char headString[];
	int counter;
	int headEnd[];
	int totalBodySize;
	public Header(){
		headStart = new int[4];
		headString = new char[6];
		headEnd = new int[4];
	}
	private void print(String s, int[] objs){
		P.pt(s);
		for (int i = 0; i < objs.length; i ++){
			P.pt(objs[i] + " ");
		}
		P.pt("\n");
	}
	
	private void print(String s, char[] objs){
		P.pt(s);
		for (int i = 0; i < objs.length; i ++){
			P.pt(objs[i] + " ");
		}
		P.pt("\n");
	}
	
	void printHeadStart(){
		print("headStart", headStart);
	}
	void printHeadString(){
		print("headString",headString);
	}
	
	void printHeadEnd(){
		print("headEnd", headEnd);
	}
	
	

}
class Record {
	int date;
	String time;
	int flag;
	int err1;
	int err2;
	int err3;
	String currency;
	String version;
	int value;
	int charNum;
	String uid;
	String cashierCode;
	
	public String toString(){
		StringBuilder sb = new StringBuilder();
		sb.append("date:");
		sb.append(date);
		sb.append("time:");
		sb.append("err1");
		sb.append(err1);
		sb.append("err2:");
		sb.append(err2);
		sb.append("err3:");
		sb.append(err3);
		sb.append("cur:");
		sb.append(currency);
		sb.append("ver:");
		sb.append(version);
		sb.append("value:");
		sb.append(value);
		sb.append("charNum:");
		sb.append(charNum);
		sb.append("uid:");
		sb.append(uid);
		sb.append("cashier:");
		sb.append(cashierCode);
		
		
		return sb.toString(); 
	}
}



class FsnFile{
	Header header;
	Record[] records;
}

public class FnsReader {
	public final byte[] b;
	public static final int REC_LENGTH_PIC =	 1644;
	public static final int REC_LENGTH_NO_PIC = 108;
	public static boolean isBitSet(byte b, int bit){
		return (b & ( 1 << bit)) != 0;
	}
	public static byte toByte(
			boolean b1,
			boolean b2,
			boolean b3,
			boolean b4,
			boolean b5,
			boolean b6,
			boolean b7,
			boolean b8
			
			){
		byte b = 0;
		if (b1) b = (byte) (b | 128);
		if (b2) b = (byte) (b | 64);
		if (b3) b = (byte) (b | 32);
		if (b4) b = (byte) (b | 16);
		if (b5) b = (byte) (b | 8);
		if (b6) b = (byte) (b | 4);
		if (b7) b = (byte) (b | 2);
		if (b8) b = (byte) (b | 1);
		return b;	
	}
	
	private byte[] cubeToByte(boolean[][] cube){
		byte[] bts = new byte[128];
		int len = 0;
		for (int i = 31; i >= 0; i --){
			for (int j = 3; j >=0 ; j--){
				int l = len;
				l = 4 * ((int)len/4 +1) - len%4 - 1;
				len++;
				bts[l] = toByte(
							cube[i][j *8],
							cube[i][j *8 + 1],
							cube[i][j *8 + 2],
							cube[i][j *8 + 3],
							cube[i][j *8 + 4],
							cube[i][j *8 + 5],
							cube[i][j *8 + 6],
							cube[i][j *8 + 7]
						);
			}
		}

		return bts;
	}
	
	public FnsReader(byte[] bs){
		b = bs;
	}

	/** Read the given binary file, and return its contents as a byte array.*/ 
	static  byte[] read(String aInputFileName){
		  P.ptl("Reading in binary file named : " + aInputFileName);
	    File file = new File(aInputFileName);
	    P.ptl("File size: " + file.length());
	    byte[] result = new byte[(int)file.length()];
	    try {
	      InputStream input = null;
	      try {
	        int totalBytesRead = 0;
	        input = new BufferedInputStream(new FileInputStream(file));
	        while(totalBytesRead < result.length){
	          int bytesRemaining = result.length - totalBytesRead;
	          //input.read() returns -1, 0, or more :
	          int bytesRead = input.read(result, totalBytesRead, bytesRemaining); 
	          if (bytesRead > 0){
	            totalBytesRead = totalBytesRead + bytesRead;
	          }
	        }
	        /*
	         the above style is a bit tricky: it places bytes into the 'result' array; 
	         'result' is an output parameter;
	         the while loop usually has a single iteration only.
	        */
	        P.ptl("Num bytes read: " + totalBytesRead);
	      }
	      finally {
	        P.ptl("Closing input stream.");
	        input.close();
	      }
	    }
	    catch (FileNotFoundException ex) {
	    	P.ptl("File not found.");
	    }
	    catch (IOException ex) {
	    	P.ptl(ex);
	    }
	    return result;
	  }
	  
	public Header getHeader(){
		Header header = new Header();
		Uint16 us[] = Uint16.get4(b, 0);
		Uint16.copyToIntArray(us, header.headStart);
		us = Uint16.get6(b, 8);
		header.headString[0] = String.valueOf(us[0].toNum()).charAt(0);
		header.headString[1] = String.valueOf(us[1].toNum()).charAt(0);
		char hasPic = 'N';
		if ((int) us[2].bts[0] == 46){
			
			 hasPic = 'Y';
		}else if ((int)us[2].bts[0] == 45){
			hasPic = 'N';
		}else{
			P.ptl("wrong head string!");
		}
		header.headString[2] = hasPic;
		header.headString[3] = us[3].toChar();
		header.headString[4] = us[4].toChar();
		header.headString[5] = us[5].toChar();
		header.printHeadStart();
		header.printHeadString();
		
		Uint32 counter = new Uint32(b[20], b[21], b[22], b[23]);
		P.ptl(counter.toNum());
		header.counter = counter.toNum();
		
		if (hasPic == 'Y'){
			header.totalBodySize = header.counter * REC_LENGTH_PIC;
		}else{
			header.totalBodySize = header.counter * REC_LENGTH_PIC;
				
		}
		us = Uint16.get4(b, 24);
		Uint16.copyToIntArray(us, header.headEnd);
		header.printHeadEnd();
		return header;
	}
	public static void main(String args[]){
		FnsReader reader = new FnsReader(FnsReader.read("data/data.FSND"));
		Header header = reader.getHeader();
		reader.getBody(header.counter);
	}
	
	void printCube(boolean[][] cube){
		for (int i = 0 ; i < 32; i ++){
			for (int j = 0; j < 32; j ++){
				if (cube[i][j]){
					P.pt("1");
				}else{
					P.pt(" ");
				}
			}
		}
	}
	
	public Record[] getBody(int counter){
		Record recs[] = new Record[counter];
		for (int index = 0; index < counter; index ++){
			Record rec = new Record();
			rec.date = Uint16.get2(b, REC_LENGTH_PIC * index + 32 ).toDate();
			rec.time = Uint16.get2(b, REC_LENGTH_PIC * index + 34 ).toTime();
			rec.flag = Uint16.get2(b, REC_LENGTH_PIC * index + 36 ).toNum();
			rec.err1 = Uint16.get2(b, REC_LENGTH_PIC * index + 38 ).toNum();
			rec.err2 = Uint16.get2(b, REC_LENGTH_PIC * index + 40 ).toNum();
			rec.err3 = Uint16.get2(b, REC_LENGTH_PIC * index + 42 ).toNum();
			rec.currency =  Uint16.arrayTostring(Uint16.get4(b, REC_LENGTH_PIC * index + 44));
			if (rec.currency.equalsIgnoreCase("CNY")){
				int v = Uint16.get2(b, REC_LENGTH_PIC * index + 52 ).toNum();
				if ( v ==  0) rec.version = "1990";
				else if (v == 1) rec.version = "1999";
				else if (v == 2) rec.version = "2005";
			}else{
				rec.version = "9999";
			}
			rec.value = Uint16.get2(b, REC_LENGTH_PIC * index + 54 ).toNum();
			rec.charNum = Uint16.get2(b, REC_LENGTH_PIC * index + 56).toNum();
			
			rec.uid = Uint16.arrayTostring(Uint16.get12(b, REC_LENGTH_PIC * index + 58));
			rec.cashierCode = Uint16.arrayTostring(Uint16.get24(b, REC_LENGTH_PIC * index + 82));
			
			int num = new Int16(b[REC_LENGTH_PIC*index + 132], b[REC_LENGTH_PIC*index + 133]).toNum();
			int height = new Int16(b[REC_LENGTH_PIC*index + 134], b[REC_LENGTH_PIC*index + 135]).toNum();
			int width = new Int16(b[REC_LENGTH_PIC*index + 136], b[REC_LENGTH_PIC*index + 137]).toNum();
			
			try {
				BufferedImage[] bis = new BufferedImage[12];
				for (int i = 0; i < 12; i ++){
					boolean[][] cube = new boolean[32][32];
					final int size = 128;
					byte[] bts = new byte[size];
					int line = 0;
					System.arraycopy(b, REC_LENGTH_PIC * index + 140 + i * size, bts, 0, size);
					for (int j = 0; j < size; j ++){
						for (int k = 0; k < 8; k ++ ){
							if (line == 32) line = 0;
							if (isBitSet(bts[j], k)){
								cube[line ++ ][(int)j/4] = true;
							}else{
								cube[line ++ ][(int)j/4] = false;

							}
						}
					}
					
					byte[] bw = {(byte)0xff, (byte)0};
					IndexColorModel blackAndWhite
					 = new IndexColorModel(
							 1,
							 2,
							 bw,
							 bw,
							 bw
							 );
					
					DataBuffer db = new DataBufferByte(cubeToByte(cube), 128);
					WritableRaster wr = Raster.createPackedRaster(db, width,
							height, 1, null);
					BufferedImage image = new BufferedImage(blackAndWhite, wr, true, null);
					bis[i] = image;
					
				}
				
				BufferedImage finalImage = new BufferedImage
						(
								width * 12,
								height,
								BufferedImage.TYPE_BYTE_BINARY
						);
				
				for (int i =0; i < 12; i ++){
					finalImage.createGraphics().drawImage(bis[i], width * i, 0, null);
				}
				
				if (finalImage != null){
					String fileName = "data/" + rec.uid + ".bmp";
					ImageIO.write(finalImage, "BMP", new File(fileName));
					
				}
				recs[index] = rec;
		}catch (Exception e){
			e.printStackTrace();
		}
	}
		return recs;
		
  }
}





























